package com.caregiver.watch.business.data.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.caregiver.watch.business.data.dao.HealthSleepDisplayDao;
import com.caregiver.watch.business.data.dao.HealthSleepRawDao;
import com.caregiver.watch.business.data.domain.ResponseObject;
import com.caregiver.watch.business.data.domain.ResponseWithData;
import com.caregiver.watch.business.data.domain.SleepResult;
import com.caregiver.watch.business.data.enums.SleepTypeEnum;
import com.caregiver.watch.business.data.model.dto.ApiResult;
import com.caregiver.watch.business.data.model.dto.HeartEcgDataAnalysisReq;
import com.caregiver.watch.business.data.model.dto.HeartEcgDataAnalysisResp;
import com.caregiver.watch.business.data.model.dto.HeartRriDataAnalysisReq;
import com.caregiver.watch.business.data.model.dto.HeartRriDataAnalysisResp;
import com.caregiver.watch.business.data.model.dto.SleepCalculationData;
import com.caregiver.watch.business.data.model.dto.SleepCalculationReq;
import com.caregiver.watch.business.data.model.dto.SleepCalculationResp;
import com.caregiver.watch.business.data.model.dto.SleepSection;
import com.caregiver.watch.business.data.model.po.HealthEcg;
import com.caregiver.watch.business.data.model.po.HealthRri;
import com.caregiver.watch.business.data.model.po.HealthSleepDisplay;
import com.caregiver.watch.business.data.model.po.HealthSleepRaw;
import com.caregiver.watch.business.data.service.AlgorithmService;
import com.caregiver.watch.business.data.utils.time.DateTimeUtils;
import com.caregiver.watch.business.user.model.po.UserDevice;
import com.caregiver.watch.business.user.service.UserDeviceService;
import com.caregiver.watch.common.constant.NumberConstants;
import com.caregiver.watch.common.utils.collection.ListUtils;
import com.caregiver.watch.common.utils.http.HttpUtils;
import com.caregiver.watch.common.utils.time.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;

import static com.caregiver.watch.business.data.helper.SleepDataHelper.calculateScore;
import static com.caregiver.watch.business.data.helper.SleepDataHelper.getDuration;
import static com.caregiver.watch.common.constant.Constants.COMMA;
import static com.caregiver.watch.common.constant.NumberConstants.ONE;

/**
 * @Description: 算法服务
 * @Author 疆戟
 * @Date 2025/9/7 19:57
 * @Version 1.0
 */
@Slf4j
@Service
public class AlgorithmServiceImpl implements AlgorithmService {

    private static final String DATA_TEMPLATE = "[%s]";
    /**
     * 睡眠计算
     */
    private static final String SLEEP_CALCULATION_URL = "/calculation/sleep";
    /**
     * 心电分析
     */
    private static final String HEART_DATA_ANALYSIS_URL = "/calculation/ecg";
    /**
     * 房颤分析
     */
    private static final String AF_DATA_ANALYSIS_URL = " /calculation/af";

    @Value("${algorithm.host}")
    private String host;

    @Value("${algorithm.account}")
    private String account;

    @Value("${algorithm.password}")
    private String password;

    @Autowired
    private HealthSleepRawDao healthSleepDao;
    @Autowired
    private HealthSleepDisplayDao healthSleepDisplayDao;
    @Autowired
    private UserDeviceService userDeviceService;

    /**
     * 入睡时间早于晚上 00:00 的计算公式:
     * 20+60*(1-|y-480|/480)+20*(1-|z-25|/25)-a/y*100
     * 入睡时间晚于晚上 00:00 的计算公式:
     * 20*(1-x/480)+60*(1-|y-480|/480)+20*(1-|z-25|/25)-a/y*100
     * x 是指距离入睡时间 00:00 的分钟差
     * y 是指总睡眠时长分钟数（结束时间减去开始时间，包含清醒时长）
     * z 是指深睡百分比（深睡时长/总睡眠时长*100）
     * a 是指清醒时长分钟数
     */
    @Override
    public String getSleepResult(String deviceId, String sleepDate) {
        if(log.isDebugEnabled()) {
            log.debug(String.format("getSleepResult %s %s", deviceId, sleepDate));
        }
        if (StringUtils.isEmpty(deviceId) || !DateTimeUtils.isValidDate(sleepDate)) {
            ResponseObject respObj = new ResponseObject();
            respObj.setReturnCode(10002);
            return JSON.toJSONString(respObj);
        }
        log.info("getSleepResult {} {}", deviceId, sleepDate);
        Long userId = userDeviceService.queryUserIdByDeviceUuid(deviceId);
        if (Objects.isNull(userId)) {
            ResponseObject respObj = new ResponseObject();
            respObj.setReturnCode(10404);
            return JSON.toJSONString(respObj);
        }
        LocalDate sleepDateTime = DateUtils.parseLocalDate(sleepDate);
        // 查询今天的睡眠数据
        HealthSleepDisplay latest = healthSleepDisplayDao.findLatest(userId, deviceId, LocalDateTime.of(sleepDateTime, LocalTime.MIN), LocalDateTime.of(sleepDateTime, LocalTime.MAX));
        if (Objects.nonNull(latest)) {
            ResponseWithData responseData = new ResponseWithData();
            responseData.setReturnCode(NumberConstants.ZERO);
            SleepResult sleep = new SleepResult();
            sleep.setDeviceId(deviceId);
            sleep.setSleepDate(sleepDate);
            sleep.setStartTime(DateUtils.format(latest.getStartTime()));
            sleep.setEndTime(DateUtils.format(latest.getEndTime()));
            SleepCalculationData sleepCalculationData = JSON.parseObject(latest.getRawData(), SleepCalculationData.class);
            // 深度睡眠时长
            sleep.setDeepSleep(latest.getDeepSleepDuration());
            // 浅睡时长
            sleep.setLightSleep(latest.getLightSleepDuration());
            // 苏醒时长
            sleep.setWeakSleep(latest.getWakeDuration());
            sleep.setEyemoveSleep(latest.getRapidEyeMovementDuration());
            // 睡眠分数
            sleep.setScore(latest.getSleepScore());
            sleep.setSleepHr(sleepCalculationData.getHr());
            responseData.setData(sleep);
            return JSON.toJSONString(responseData);
        } else {
            ResponseObject respObj = new ResponseObject();
            respObj.setReturnCode(10404);
            return JSON.toJSONString(respObj);
        }
    }


    @Override
    public void sleepDataCalculation(List<UserDevice> userDevices) {
        if (CollectionUtils.isEmpty(userDevices)) {
            return;
        }
        for (UserDevice userDevice : userDevices) {
            // 查询昨天12点到今天12点的睡眠数据睡眠数据
            LocalDateTime endTime = LocalDateTime.now().with(LocalTime.of(12, 0));
            LocalDateTime startTime = endTime.minusDays(1);
            List<HealthSleepRaw> sleepList =
                    healthSleepDao.findByUserIdAndTimeRange(userDevice.getUserId(), userDevice.getDeviceUuid(), startTime, endTime);
            if (CollectionUtils.isEmpty(sleepList)) {
                log.info("用户{}设备{}没有睡眠数据...", userDevice.getUserId(), userDevice.getDeviceUuid());
                continue;
            }
            // 今天的睡眠数据 和 昨天的睡眠数据
            List<HealthSleepRaw> todaySleepList = new ArrayList<>(), yesterdaySleepList = new ArrayList<>();
            LocalDate today = DateUtils.today();
            for (HealthSleepRaw sleep : sleepList) {
                LocalDateTime recordedAt = sleep.getRecordedAt();
                if (Objects.isNull(recordedAt)) {
                    continue;
                }
                if (today.equals(recordedAt.toLocalDate())) {
                    todaySleepList.add(sleep);
                } else {
                    yesterdaySleepList.add(sleep);
                }
            }
            LocalDate now = LocalDate.now();
            LocalDateTime start = LocalDateTime.of(now, LocalTime.MIN);
            LocalDateTime end = LocalDateTime.of(now, LocalTime.MAX);
            List<HealthSleepDisplay> healthSleepDisplays = healthSleepDisplayDao.queryByUserIdAndTimeRange(userDevice.getUserId(), userDevice.getDeviceUuid(), start, end);
            if (!CollectionUtils.isEmpty(healthSleepDisplays)) {
                log.info("用户{}设备{}今天已经保存过睡眠数据...", userDevice.getUserId(), userDevice.getDeviceUuid());
                return;
            }
            SleepCalculationReq request = buildSleepCalculationReq(userDevice, today, todaySleepList, yesterdaySleepList);
            try {
                SleepCalculationResp resp = HttpUtils.post(host + SLEEP_CALCULATION_URL, JSON.toJSONString(request), new TypeReference<SleepCalculationResp>() {
                });
                if (!resp.isSuccess()) {
                    log.error("睡眠计算失败:{}", resp.getMessage());
                    continue;
                }
                saveHealthSleepDisplay(userDevice, resp.getData());
            } catch (Exception e) {
                log.error("调用睡眠计算接口异常...", e);
            }
        }
    }

    /**
     * 构建睡眠计算请求
     * @param userDevice
     * @param today
     * @param todaySleepList
     * @param yesterdaySleepList
     * @return
     */
    private SleepCalculationReq buildSleepCalculationReq(UserDevice userDevice,
                                                         LocalDate today,
                                                         List<HealthSleepRaw> todaySleepList,
                                                         List<HealthSleepRaw> yesterdaySleepList) {
        SleepCalculationReq request = new SleepCalculationReq();
        request.setDeviceId(userDevice.getDeviceUuid());
        request.setAccount(account);
        request.setPassword(password);
        request.setPrevDay(String.format(DATA_TEMPLATE, ListUtils.join(ListUtils.map(yesterdaySleepList, HealthSleepRaw::getRawData), COMMA)));
        request.setNextDay(String.format(DATA_TEMPLATE, ListUtils.join(ListUtils.map(todaySleepList, HealthSleepRaw::getRawData), COMMA)));
        request.setRecordDate(Integer.valueOf(DateUtils.format(today, DateUtils.YYYYMMDD)));
        return request;
    }


    /**
     * 保存睡眠数据
     * @param userDevice
     * @param data
     */
    private void saveHealthSleepDisplay(UserDevice userDevice, SleepCalculationData data) {
        if (Objects.isNull(data)) {
            return;
        }
        // 处理睡眠计算结果
        HealthSleepDisplay sleepDisplay = new HealthSleepDisplay();
        sleepDisplay.setUserId(userDevice.getUserId());
        sleepDisplay.setDeviceUuid(userDevice.getDeviceUuid());
        sleepDisplay.setSleepStatus(ONE);
        sleepDisplay.setStartTime(data.getStartTime());
        sleepDisplay.setEndTime(data.getEndTime());
        // 计算睡眠时长 单位分钟
        List<SleepSection> sections = data.getSections();
        // 将数据根据睡眠类型进行分组
        Map<SleepTypeEnum, List<SleepSection>> sleepTypeToSleepDataMap = ListUtils.groupingBy(sections, sleepSection -> SleepTypeEnum.pares(sleepSection.getType()));
        // 深度睡眠时长
        Integer deepSleepDuration = getDuration(sleepTypeToSleepDataMap.get(SleepTypeEnum.DEEP_SLEEP));
        sleepDisplay.setDeepSleepDuration(deepSleepDuration);
        // 浅睡时长
        Integer lightSleepDuration = getDuration(sleepTypeToSleepDataMap.get(SleepTypeEnum.LIGHT_SLEEP));
        sleepDisplay.setLightSleepDuration(lightSleepDuration);
        // 快速眼动时长
        Integer rapidEyeMovementDuration = getDuration(sleepTypeToSleepDataMap.get(SleepTypeEnum.FAST_EYE_MOVEMENT));
        sleepDisplay.setRapidEyeMovementDuration(rapidEyeMovementDuration);
        // 苏醒时长
        Integer wakeDuration = getDuration(sleepTypeToSleepDataMap.get(SleepTypeEnum.WAKE_UP));
        sleepDisplay.setWakeDuration(wakeDuration);
        // 睡眠总时长
        int sleepDuration = deepSleepDuration + lightSleepDuration + rapidEyeMovementDuration + wakeDuration;
        sleepDisplay.setDuration(sleepDuration);
        // 睡眠总得分
        sleepDisplay.setSleepScore(calculateScore(data, LocalDate.now(), deepSleepDuration, lightSleepDuration));
        sleepDisplay.setRawData(JSON.toJSONString(data));
        sleepDisplay.setCreatedAt(DateUtils.now());
        int insert = healthSleepDisplayDao.insert(sleepDisplay);
        log.info("insertSleepData:{}", insert);
    }


    /**
     * 心电分析
     */
    @Override
    public HeartEcgDataAnalysisResp heartDataAnalysis(String deviceUuid, List<HealthEcg> ecgList) {
        if (CollectionUtils.isEmpty(ecgList)) {
            return null;
        }
        // 合并心电数据
        List<Integer> ecgData = ListUtils.flatMapDistinct(ecgList, ecg -> {
            // 获取原始数据
            List<Integer> rawData = JSON.parseArray(ecg.getRawData(), Integer.class);
            return ListUtils.ofNullable(rawData, Stream.empty());
        });
        HeartEcgDataAnalysisReq req = HeartEcgDataAnalysisReq.build(deviceUuid, account, password, ecgData);
        try {
            ApiResult<HeartEcgDataAnalysisResp> response = HttpUtils.post(host + HEART_DATA_ANALYSIS_URL, JSON.toJSONString(req), new TypeReference<ApiResult<HeartEcgDataAnalysisResp>>() {
            });
            if (response.isSuccess()) {
                return response.getData();
            }
            log.error("调用心电分析接口异常:{}", response.getMessage());
        } catch (Exception e) {
            log.error("调用心电分析接口异常...", e);
        }
        return null;
    }

    @Override
    public HeartRriDataAnalysisResp atrialFibrillationDataAnalysis(String deviceUuid, List<HealthRri> rris) {
        if (CollectionUtils.isEmpty(rris)) {
            return null;
        }
        List<Integer> rriList = ListUtils.flatMapDistinct(rris, rri -> {
            // 获取原始数据
            List<Integer> rawData = JSON.parseArray(rri.getRawData(), Integer.class);
            return ListUtils.ofNullable(rawData, Stream.empty());
        });
        HeartRriDataAnalysisReq req = HeartRriDataAnalysisReq.build(deviceUuid, account, password, rriList);
        try {
            ApiResult<HeartRriDataAnalysisResp> response = HttpUtils.post(host + AF_DATA_ANALYSIS_URL, JSON.toJSONString(req), new TypeReference<ApiResult<HeartRriDataAnalysisResp>>() {
            });
            if (response.isSuccess()) {
                return response.getData();
            }
            log.error("调用心电分析接口异常:{}", response.getMessage());
        } catch (Exception e) {
            log.error("调用心电分析接口异常...", e);
        }
        return null;
    }

}
